package com.b2c.open.gateway;

import com.b2c.open.gateway.dao.GatewayRouterMapper;
import com.b2c.open.gateway.entity.GatewayRouter;
import com.b2c.open.gateway.filter.ThirdAuthGatewayFilter;
import com.b2c.open.gateway.filter.ResponseGatewayFilter;
import com.b2c.open.gateway.utils.RSAUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.factory.HystrixGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;

@EnableConfigurationProperties
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan({"com.b2c.open.gateway.dao"})
@SpringBootApplication
@EnableHystrix
public class OpenGatewayApplication {

    private static final Logger logger = LoggerFactory.getLogger(OpenGatewayApplication.class);
    @Autowired
    private GatewayRouterMapper gatewayRouterMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public static void main(String[] args) {
        SpringApplication.run(OpenGatewayApplication.class, args);
    }

    /**
     * 初始化三方应用过滤器
     * @return
     */
    @Bean
    public ThirdAuthGatewayFilter thirdAuthGatewayFilter(){
        return new ThirdAuthGatewayFilter();
    }

    /**
     * 初始化返回值过滤器
     * @return
     */
    @Bean
    public ResponseGatewayFilter responseGatewayFilter(){
        if(!stringRedisTemplate.hasKey("open:gateway:privateKey") || !stringRedisTemplate.hasKey("open:gateway:publicKey")){
            try {
                Map<String, Object> keyMap= RSAUtils.genKeyPair();
                String privateKey = RSAUtils.getPrivateKey(keyMap);
                String publicKey = RSAUtils.getPublicKey(keyMap);
                logger.info("初始化私钥: {}",privateKey);
                logger.info("初始化公钥: {}",publicKey);
                stringRedisTemplate.opsForValue().set("open:gateway:privateKey", privateKey);
                stringRedisTemplate.opsForValue().set("open:gateway:publicKey", publicKey);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new ResponseGatewayFilter();
    }

    @Bean
    public RedisRateLimiter redisRateLimiter(){
        return new RedisRateLimiter(1,1);
    }


    /**
     * 第三方应用路由
     * @param builder
     * @return
     */
    @Bean
    @RefreshScope
    public RouteLocator thirdAppRouteLocator(RouteLocatorBuilder builder) {
        List<GatewayRouter> routers = gatewayRouterMapper.selectByType(2);
        logger.info("三方应用路由规则初始化: {}", routers);
        RouteLocatorBuilder.Builder b = builder.routes();
        routers.stream().filter(r -> r.getStripPrefix() != null && r.getStripPrefix() >= 0)
                .forEach(t -> b.route(
                        r -> r.path(t.getPath())
                                .filters(
                                        f -> f.filters(thirdAuthGatewayFilter(), responseGatewayFilter())
                                                .stripPrefix(t.getStripPrefix())
                                                .hystrix(config -> config.setName("myHystrix").setFallbackUri("forward:/fallback"))
                                                .requestRateLimiter(config -> config
                                                        .setRateLimiter(redisRateLimiter())
                                                        .setKeyResolver((KeyResolver) exchange -> {
                                                            return Mono.just(exchange.getRequest().getURI().getPath());
                                                        })
                                                    )
                                    )
                                .uri(t.getUri())));
        return b.build();
    }
}
